//
// File peer.h
//
// This file defines the four public methods of class Peer, together 
// with associated helper classes and constants
//

// Various limits

#ifndef PEER_H
#define PEER_H

#include <string>
#include "Common.h"
#include "Status.h"
#include "Jobs.h"
#include "File.h"
#include "peerInfo.h"

using namespace std;

// Peer and Status are the classes we really care about
// Peers is a container; feel free to do a different container

class Peer {
    // This is the formal interface and you should follow it
public:
    Peer(string ip, int port, int capacity);
    ~Peer();
    int insert(string filename);
    int query(Status& status);
    int join();     // Note that we should have the peer list, so it is not needed as a parameter
    int leave();
    bool initThreads();
    int workerImplementation();
    int listenerImplementation();
    int shareImplementation();

    int changeLocalDirectory(string newDirectory);

    enum Message { JOIN, LEAVE, QUERYSTATUS, QUERYCHUNK, RECEIVEDCHUNK, WANTCHUNK, DONTWANTCHUNK, SENDCHUNK, NEWFILE, REPLYSTATUS,
                    EXITLISTENER, SYSREQUEST, SAVESUCCESS, TAGSUCCESS, REQUESTFILE, REQUESTUPDATE,
                    SAVEFILE, TAGFILE, NEWVERSION, NEWMODIFIED };

    // increased coupling for this method: now message takes in params = job->args
    int createMessage(Message type, void *params, char *mail, int &length);

    int getChunk(char *fileName, int chunkNum, char *result, int &size);
    int writeChunk(string fileName, int chunkNum, char *content, int size);
    int combineFile(string fileName, int chunks);
    
    // acquires and releases mutex locks
    int addFile(string fileName, int chunks, bool complete);
    int addChunk(string fileName, int chunkIndex);  
    int addSystemChunk(string fileName, int chunk);
    int removeSystemChunk(string fileName, int chunk);
    int setShare(bool state);
    int pushJob(Job * job);

    int displayNumPeers();

    void display();

private:
    int joinNetwork(); // The peersFile is the name of a file that contains a list of the peers
                                      // Its format is as follows: in plaintext there are up to maxPeers lines,
                                      // where each line is of the form: <IP address> <port number>
                                      // This file should be available on every machine on which a peer is started,
                                      // though you should exit gracefully if it is absent or incorrectly formatted.
                                      // After execution of this method, the _peers should be present.

    int queryPeers(char * message, int messageLen, void *This);
    int broadcast(char * message, int messageLen);
    // Peer operator()(int i);
    peerInfo *getPeer(int index);
    int numPeers();
    int numConnected();
    int peerJoin(string ip, int port);
    int peerLeave(string ip, int port);

    int initializePeersInfo(string peerList);

    int _numConnected;

    pthread_mutex_t index_mutex;
    pthread_mutex_t peersArray_mutex;
    pthread_mutex_t numConnected_mutex;

    // only changed when reading in peerlist
    int _numPeers;

    int _curIndex;

    peerInfo *localInfo;
    peerInfo *_peers[maxPeers];

    string _localDirectory;
    Status _status;
    string _peerList; // maybe this should be passed in constructor...
    bool _rejoin;

    sem_t share_lock;
    sem_t job_queue_count;
    bool _sharing;
    pthread_mutex_t job_queue_mutex;
    pthread_mutex_t status_mutex;
    pthread_mutex_t sharing_mutex;
    pthread_mutex_t transit_mutex;

    pthread_mutex_t host_machine_list_mutex;

    pthread_mutex_t file_writing_mutex;

    queue<Job *> jobs;

    vector<pthread_t *> _workerContext;
    pthread_t _listenerContext;
    pthread_t _shareContext;

    int process_message(int client_sock, char *buf, int bufLen);

    static void *listener(void *This);
    static void *worker(void *This);
    static void *share(void *This);

    int getNextChunkToShare(string &fileName, int &chunk);

    int process_job(Job *job);

    string convertInt(int number);

    bool isChunkInProgress(string name, int chunk);
    int newChunkInProgress(string name, int chunk);
    int chunkTransferCompleted(string name, int chunk);
    int sendMessage (string IP, int port, char * message, int messageLen);

    vector<ChunkRequestArgs *>transit;

    list<peerInfo *> hostMachineList;

    

    // [*] displayJobQueue() for debugging purposes -> make it a command line command
    // [*] createJob() method!!!!!!
};

#endif
